import io
import os
import time
import uuid
import datetime
import logging
import random
import hashlib
import base64
import dateutil.tz as tz
from urllib.parse import unquote


class CommonUtil(object):

    @staticmethod
    def clean_redis_data(redis_value):
        if isinstance(redis_value, bytes):
            redis_value = redis_value.decode()
        return redis_value

    @staticmethod
    def rgb_to_hex(rgb):
        try:
            color = "#"
            for i in rgb:
                num = int(i)
                color += str(hex(num))[-2:].replace('x', '0').upper()
            return color
        except Exception as ex:
            logging.info("rgb颜色转换16进制异常，原因：%s" % str(ex))
            return "#65C5FF"

    @staticmethod
    def get_picture_base64_data(image_path):
        try:
            with open(image_path, 'rb') as img_obj:
                base64_data = base64.b64encode(img_obj.read()).decode("utf8")
            return base64_data
        except Exception as ex:
            logging.info("读取图片成base64时遇到异常，原因：%s" % str(ex))
            return None

    @staticmethod
    def str_index_of(message, sub):
        try:
            return message.index(sub)
        except Exception as ex:
            logging.info("str_index_of错误，原因：%s" % ex)
            return -1

    @staticmethod
    def generate_guid():
        return uuid.uuid1()

    @staticmethod
    def convert_json_to_str(data):
        return str(data).replace("'", '"')

    @staticmethod
    def remove_file(path):
        try:
            os.remove(path)
        except Exception as ex:
            logging.info("删除文件：%s失败， 原因：%s" % (str(path), str(ex)))
            return False

    @staticmethod
    def unquote_string(s):
        return unquote(s)

    @staticmethod
    def convert_currency_unit(value):
        if not value:
            return value
        return value / 100

    @staticmethod
    def sha256_encode(string):
        return hashlib.sha256(string.encode('utf-8')).hexdigest()

    @staticmethod
    def get_quarter_by_month(month=None):
        now = datetime.datetime.now()
        month = now.month if not month else month
        if month < 4:
            return 1
        _x = month % 4
        quarter = (month // 4) + _x
        return quarter

    @staticmethod
    def get_quarter_timestamp(quarter=None, year=None, month=None):
        year = CommonUtil.get_current_year() if not year else year
        month = CommonUtil.get_current_month() if not month else month
        quarter = CommonUtil.get_quarter_by_month(month) if not quarter else quarter
        month_start_end_dict = {
            1: [1, 3],
            2: [4, 6],
            3: [7, 9],
            4: [10, 12]
        }
        start_m, end_m = month_start_end_dict.get(quarter)
        start, _ = CommonUtil.get_same_month_timestamp(year=year, month=start_m)
        _, end = CommonUtil.get_same_month_timestamp(year=year, month=end_m)
        return start, end

    @staticmethod
    def compute_year_on_year_growth(number, last_number):
        if not number:
            if last_number:
                return "up", "100.00%"
            return "", "-"
        diff_value = last_number - number

        if diff_value > 0:
            mode = "up"
        elif diff_value < 0:
            mode = "down"
        else:
            mode = ""
        diff_value = abs(diff_value)

        if mode == "":
            return mode, "-"

        result = '%.2f' % ((diff_value * 100) / number)
        if mode == "up":
            return mode, f"{result}%"
        return mode, f"{result}%"

    @staticmethod
    def bytes_str(token):
        if isinstance(token, bytes):
            token = token.decode("utf8")
        return token

    @staticmethod
    def get_md5_by_str(string):
        data_md5 = hashlib.md5(base64.b64decode(string)).hexdigest()
        return data_md5

    @staticmethod
    def open_file(path):
        try:
            with open(path, 'rb') as f:
                res = f.read()
        except Exception as ex:
            logging.info("打开文件：%s出现异常，原因：%s" % (path, str(ex)))
            res = None
        return res

    @staticmethod
    def compare_list(new_list, exist_list):
        """
        在m2m关系中，计算需要新增和删除的数据
        """
        add_list, delete_list = [], []
        for item in new_list:
            if item not in exist_list:
                add_list.append(item)
        for item in exist_list:
            if item not in new_list:
                delete_list.append(item)
        return add_list, delete_list

    @staticmethod
    def get_bytes_io_by_file_obj(file_obj):
        """
        file对象转二进制io流文件对象
        """
        bytes_io = io.BytesIO()
        bytes_io.write(file_obj)
        return bytes_io

    @staticmethod
    def get_md5_value(string):
        try:
            return hashlib.md5(string.encode()).hexdigest()
        except Exception as ex:
            logging.info("md5时出现异常，原因：%s" % str(ex))
            return ""

    @staticmethod
    def get_md5_bytes_value(string):
        try:
            return hashlib.md5(string).hexdigest()
        except Exception as ex:
            logging.info("md5时出现异常，原因：%s" % str(ex))
            return ""

    @staticmethod
    def get_current_year_month_day():
        year = datetime.datetime.now().year
        month = datetime.datetime.now().month
        day = datetime.datetime.now().day
        return year, month, day

    @staticmethod
    def get_path_by_system_version(path_list):
        if os.name == 'nt':
            return "\\".join(path_list)
        return "/".join(path_list)

    @staticmethod
    def convert_timestamp_to_dt(timestamp, f="%Y-%m-%d %H:%M:%S"):
        if not timestamp:
            return None
        try:
            dt = datetime.datetime.fromtimestamp(timestamp)
            # from_zone = tz.tzutc()
            to_zone = tz.tzlocal()
            # utc = dt.replace(tzinfo=from_zone)  # 先行替换datetime的时区到utc时区
            local = dt.astimezone(to_zone)
            return local.strftime(f)
        except Exception as ex:
            logging.info("转换时间戳到时间格式时出现错误，原因：%s" % str(ex))
            return None

    @staticmethod
    def get_timestamp_thirteen():
        return int(round(time.time() * 1000))

    @staticmethod
    def get_time_array_thirteen(timestamp):
        try:
            r = time.localtime(timestamp / 1000)
            return r
        except Exception as ex:
            logging.info("转换时间戳到时间格式时出现错误，原因：%s" % str(ex))
            return None

    @staticmethod
    def get_statistic_condition(conditions, field_list):
        statistic_condition = []
        for con in conditions:
            if con and con.get("field", None) not in field_list:
                statistic_condition.append(con)
        return statistic_condition

    @staticmethod
    def check_is_number(v):
        try:
            v = str(v)
            if not v:
                return v
            if v[0] == '-':
                v = v[1:]
            return v.isdigit()
        except Exception as ex:
            logging.info("check_is_number函数出现错误，原因：%s" % str(ex))
            return False

    @staticmethod
    def random_str(length=32):
        chars = 'abcdefghijklmnopqrstuvwxyz0123456789'
        rand = random.Random()
        return "".join([chars[rand.randint(0, len(chars) - 1)] for _ in range(length)])

    @staticmethod
    def random_digit(length=4):
        res = []
        for i in range(length):
            r = random.randint(0, 9)
            res.append(str(r))
        return "".join(res)

    @staticmethod
    def private_phone(phone):
        if not phone:
            return None
        phone = str(phone)
        if len(phone) != 11:
            return None
        return "%s****%s" % (phone[:3], phone[7:])

    @staticmethod
    def composetime(nyrsfm_time):
        time1 = datetime.datetime.strptime(nyrsfm_time, "%Y-%m-%d %H:%M:%S")
        time2 = int(time.mktime(time1.timetuple()))
        return time2

    @staticmethod
    def _replace_digit(d):
        if not d:
            return d
        if len(str(d)) == 1:
            return "0%s" % d
        return d

    @staticmethod
    def random_number(num2):
        list2 = []
        for number in range(num2):
            str2 = str(random.randint(0, 9))
            list2.append(str2)
        b = " ".join(list2).replace(" ", "")
        return b

    @staticmethod
    def get_out_trade_no(original_no=None):
        original_no = "" if not original_no else original_no
        f_str_datetime = CommonUtil.get_f_str_datetime()
        random_num = CommonUtil.random_number(7)
        return "%s%s%s" % (original_no, f_str_datetime, random_num)

    @staticmethod
    def get_timestamp_detail():
        return int(round(CommonUtil.get_timestamp() * 1000))

    @staticmethod
    def get_timestamp():
        return int(datetime.datetime.now().timestamp())

    @staticmethod
    def get_f_str_datetime():
        return datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')[:-3]

    @staticmethod
    def get_week_timestamp():
        now = datetime.datetime.now()
        zero_week = now - datetime.timedelta(days=now.weekday(), hours=now.hour, minutes=now.minute, seconds=now.second,
                                             microseconds=now.microsecond)
        last_week = now + datetime.timedelta(days=6 - now.weekday(), hours=23 - now.hour, minutes=59 - now.minute,
                                             seconds=59 - now.second)

        return int(zero_week.timestamp()), int(last_week.timestamp())

    @staticmethod
    def get_today_timestamp():
        now = datetime.datetime.now()
        zero_today = now - datetime.timedelta(hours=now.hour, minutes=now.minute, seconds=now.second,
                                              microseconds=now.microsecond)
        return int(zero_today.timestamp())

    @staticmethod
    def get_last_today_timestamp():
        now = datetime.datetime.now()
        zero_today = now - datetime.timedelta(hours=now.hour, minutes=now.minute, seconds=now.second,
                                              microseconds=now.microsecond)
        last_today = zero_today + datetime.timedelta(hours=23, minutes=59, seconds=59)
        return int(last_today.timestamp())

    @staticmethod
    def get_same_month_timestamp(month=None, year=None):
        now = datetime.datetime.now()
        month = now.month if not month else month
        year = now.year if not year else year
        zero_month = datetime.datetime(year, month, 1)
        if month < 12:
            month += 1
        else:
            year += 1
            month = 1
        last_month = datetime.datetime(year, month, 1) - datetime.timedelta(days=1) + datetime.timedelta(
            hours=23, minutes=59, seconds=59)
        return int(zero_month.timestamp()), int(last_month.timestamp())

    @staticmethod
    def get_same_year_timestamp(year=None):
        now = datetime.datetime.now()
        year = now.year if not year else year
        this_year_start = datetime.datetime(year, 1, 1)
        this_year_end = datetime.datetime(year + 1, 1, 1) - datetime.timedelta(days=1)
        return int(this_year_start.timestamp()), int(this_year_end.timestamp())

    @staticmethod
    def get_current_year():
        now = datetime.datetime.now()
        return now.year

    @staticmethod
    def get_current_month():
        now = datetime.datetime.now()
        return now.month
